#include<iostream>
#include<string>
#include<cstring>
#include<unistd.h>
#include<sys/types.h>     
#include<sys/socket.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unordered_map>
#include"Log.hpp"

#define SIZE 1024

using namespace std;

Log log;

const string defaultip = "0.0.0.0";
const uint16_t defaultport = 8080;

enum
{
    CREATE_ERROR = 1,
    BING_ERROR = 2
};

class UdpServer
{
public:
    UdpServer(int sockfd = -1, const string& ip = defaultip, const uint16_t& port = defaultport)
    :_sockfd(sockfd), _ip(defaultip), _port(defaultport)
    {}

    void ServerInit()
    {
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if(_sockfd < 0)
        {
            log(Fatal, "create socket fail, errno:%d, strerror:%s", errno, strerror(errno));
            exit(CREATE_ERROR);
        }

        log(Info, "success create socket, sockfd:%d", _sockfd);

        struct sockaddr_in server;
        socklen_t len = sizeof(server);
        server.sin_family = AF_INET;
        server.sin_port = htons(_port);
        server.sin_addr.s_addr = inet_addr(_ip.c_str());

        if(bind(_sockfd, (struct sockaddr*)&server, len) < 0)
        {
            log(Fatal, "bind error, errno:%d, strerror:%s", errno, strerror(errno));
            exit(BING_ERROR);
        }

        log(Info, "success bind socket, sockfd...");
    }

    void CheckUser(const struct sockaddr_in& client, const string& clientip, const uint16_t& clientport)
    {
        auto it = online_user.find(clientip);
        if(it == online_user.end())
        {
            //找到结尾说明用户不在哈希桶中, 需要添加新用户
            online_user.insert({clientip, client});
            cout << "[" << clientip << ":" << clientport << "] add to online user." << endl;
        }
    }

    //把server收到的消息广播给所有人 -> 向容器内部保存的任何客户端发送, 聊天是就是服务器把收到的消息转发给任意一个注册登陆过的客户端
    void BroadCast(const string& info, const string& clientip, const uint16_t& clientport)
    {
        string message;
        message += "[";
        message += clientip;
        message += ":";
        message += to_string(clientport);
        message += "]# ";
        message += info;

        //把消息转发给所有已经向服务器注册过的客户端
        for(const auto& user:online_user)
        {
            socklen_t len = sizeof(user.second);
            sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&(user.second), len);
        }
    }

    void ServerRun()
    {
        char buffer[SIZE];

        while(true)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);

            int n = recvfrom(_sockfd, buffer, sizeof(buffer)- 1, 0, (struct sockaddr*)&client, &len);
            if(n > 0)
            {
                buffer[n] = '\0';
            }

            uint16_t clientport = ntohs(client.sin_port);
            string clientip = inet_ntoa(client.sin_addr);

            CheckUser(client, clientip, clientport);

            string info = buffer;
            BroadCast(info, clientip, clientport);
        }
    }

    ~UdpServer()
    {}
private:
    int _sockfd;
    string _ip;
    uint16_t _port;

    unordered_map<string, struct sockaddr_in> online_user;
};